#!/bin/bash

usage() {
   [ -n "$*" ] && echo "$*"
   cat <<EOF

Get XML weather data from FMI's Open data service ($urlbase).
A location ID from geonames is required.
Dependencies: ${deps[@]}

Usage: $me [options] location_id

Options:

-l str  language (default: $lang), possible values: fin,eng

-t int  Timespan in hours. Default: $timespan

-t int  Offset of first forecast, in hours. Can be negative.

-q str  Type of forecast, get a list with ./wfs_describeStoredQueries
        Default: $query

-p str  Forecast parameters. Get a list of possible values with "-P"

Options -P, -U and -R will exit without parsing the forecast data.

-P      Only get all possible forecast parameters for query & location.

-U      Only print URL.

-R      Only output raw XML received.

-f str  Parse local file. Invalidates other options. Mostly for development.

EOF
   exit 1
}

deps=( curl date xmlstarlet )

timespan=3
offset=0
file=""
lang=eng
# type of forecast, get a list with ./wfs_describeStoredQueries
query="fmi::forecast::harmonie::surface::point::simple"
urlbase="https://opendata.fmi.fi"
wfs_req="wfs?service=WFS&version=2.0.0&request=getFeature&storedquery_id"
# List parameters only
P=0
# spit out URL only
urlonly=0
# spit out raw data only
raw=0
# Separator, used in labels file
sep=" %% "
iconbase_ws3="icons/opendata-resources/symbols/" # Path where icons for weathersymbol3 are stored
iconbase_ss="icons/smartsymbol/v31/p/" # Path where icons for smartsymbol are stored
iconext=".svg"

### PARAMETERS ARE RETURNED IN THE SAME ORDER AS REQUESTED

# sane choices
parameters="totalcloudcover,cloudsymbol,temperature,humidity,dewpoint,fogsymbol,visibility,precipitation1h,precipitationamount,pressure,windspeedms,maximumwind,windgust,winddirection,weathersymbol3"

# saner choices: remove useless because unknown symbols
#~ parameters="totalcloudcover,temperature,humidity,dewpoint,visibility,precipitation1h,precipitationamount,pressure,windspeedms,maximumwind,windgust,winddirection,weathersymbol3"

#~ parameters="Temperature,Precipitation1h,PrecipitationAmount,WindDirection,WindSpeedMS,MaximumWind,WindGust,Humidity,TotalCloudCover,LowCloudCover,MediumCloudCover,HighCloudCover,Pressure,smartsymbol,pop"

while getopts "l:f:t:o:q:p:PURh" opt; do
  case $opt in
    l) [[ "$OPTARG" =~ ^(fin|eng)$ ]] && lang="$OPTARG" || usage "Invalid language: $OPTARG"
    ;;
    f) [ -r "$OPTARG" ] && file="$OPTARG" || usage "cannot read $OPTARG"
    ;;
    t) [[ "$OPTARG" =~ [1-9][0-9]* ]] && (( OPTARG <= 36 )) || usage "Option -${opt}: invalid number $OPTARG"
       timespan="$OPTARG"
    ;;
    o) [[ "$OPTARG" =~ [1-9][0-9]* ]] && (( OPTARG <= 36 )) || usage "Option -${opt}: invalid number $OPTARG"
       timespan="$OPTARG"
    ;;
    q) query="$OPTARG"
    ;;
    p) parameters="$OPTARG"
    ;;
    P) P=1
       timespan=1
    ;;
    U) urlonly=1
    ;;
    R) raw=1
    ;;
    *) usage
      ;;
  esac
done
shift $((OPTIND-1))

[[ "$0" == *'/'* ]] && { cd "${0%/*}" || exit 1; }

dir="files/${0##*/}_data"
mkdir -p "$dir" || exit 1
symbol="files/symbol_text_language" # descriptions for smartsymbol, replace "language" with $lang

# File contains ObservableProperty labels in either english or finnish.
# from https://opendata.fmi.fi/meta?observableProperty=forecast&language=$lang
labels="$dir/labels_$lang"
# Won't be downloaded if it exists.
if ! [ -r "$labels" ]; then
URL="$urlbase/meta?observableProperty=forecast&language=$lang"
echo "# Retrieved from $URL" >"$labels"
curl -s "$URL" | \
xmlstarlet sel -t -m "/_:CompositeObservableProperty/_:component/_:ObservableProperty" -v "_:basePhenomenon" -o " (" -v "normalize-space(_:label)" -o ")$sep" -v "@gml:id" -o "$sep" -m "_:uom" -v "@uom" -n >>"$labels"
# Each line contains: Localised Name %% ParameterName %% Unit of measurement
fi
# Read labels file into array (discard first line, it's a comment)
mapfile -s 1 -t labels <"$labels"

if [ -n "$file" ]; then
   weather="$(<"$file")"
else
   [ -n "$geoid" ] && geoid="$1" || geoid=658225 # Helsinki
   [[ "$geoid" =~ [0-9]+ ]] || usage "Parameter \"$geoid\" is not a numerical geoid. Use ./get_location_id to get one."

   starttime="$(date -u '+%FT%R:%SZ' --date="$offset hours")" # starting at now +/- offset
   endtime="$(date -u '+%FT%R:%SZ' --date="$((timespan + offset)) hours")" # assuming we want a $timespan hours forecast
   timestep=60 # minutes

   URL="$urlbase/$wfs_req=${query}&geoid=${geoid}&starttime=${starttime}&endtime=${endtime}&timestep=${timestep}"

   if [[ "$P" == 1 ]]; then
      [[ "$urlonly" == 1 ]] && echo "URL: $URL"
      parameters="$(curl -s "$URL" | xmlstarlet sel -t -m "//wfs:member/BsWfs:BsWfsElement" -v "BsWfs:ParameterName" -o ',')"
      parameters="${parameters%,}" # remove the last trailing comma
      echo "Parameters: ${parameters,,}" # make it all lowercase
      exit
   fi

   URL="${URL}&parameters=${parameters,,}" # parameters should be all lowercase

   [[ "$urlonly" == 1 ]] && echo "URL: $URL"
   [[ "$raw" == 1 ]] && curl "$URL"
   { [[ "$urlonly" == 1 ]] || [[ "$raw" == 1 ]]; } && exit

   weather="$(curl -s "$URL")"
fi

# Parse query response XML, read into array:
# 0: Time
# 1: ParameterName
# 2: ParameterValue etc...
mapfile -t array <<<"$(xmlstarlet sel -t -m "/wfs:FeatureCollection/wfs:member/BsWfs:BsWfsElement" -v "BsWfs:Time" -n -v "BsWfs:ParameterName" -n -v "BsWfs:ParameterValue" -n <<<"$weather")"

# Forecast, simple text output
oldtime=''
for ((i=0;i<${#array[@]};i+=3)); do
   if [[ "$oldtime" != "${array[i]}" ]]; then
      # Only here is UTC ISO 8601 time converted to local time
      date '+%n%H:%Mh%n¯¯¯¯¯¯' --date="${array[i]}"
      oldtime="${array[i]}"
   fi
   # Replace weathersymbol3 with path to corresponding image
   [[ "${array[i+1]}" == weathersymbol3 ]] && echo "$iconbase_ws3${array[i+2]%.*}$iconext" && continue
   # Replace smartsymbol with path to corresponding image
   [[ "${array[i+1]}" == smartsymbol ]] && echo "$iconbase_ss${array[i+2]%.*}$iconext => $(grep -w ^${array[i+2]%.*} "${symbol/language/${lang:0:2}}" |cut -d: -f2)" && continue
   # Replace ParameterName with localised label
   for ((j=0,found=0;j<${#labels[@]};j++)); do
      [[ "${labels[j]}" == *"$sep${array[i+1]}$sep"* ]] && echo "${labels[j]%%$sep*} ${array[i+2]} ${labels[j]##*$sep}" && found=1 && break
   done
   [[ "$found" == 0 ]] && echo "${array[i+1]}: ${array[i+2]}"
done
